package frpc.manager.set;

import frpc.manager.basics.RedisBasicsBusinessManager;
import frpc.manager.lock.IRedisLockManager;
import frpc.manager.operation.GiveCacheData;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

/**
 * 类描述:  Redis——Set对象处理实现
 *
 * @author 王雷
 * @version 1.0.0
 * @date 2022/6/2 0002 下午 3:12
 */
@SuppressWarnings("unchecked")
public class RedisSetManagerImpl extends RedisBasicsBusinessManager implements IRedisSetManager {

    /**
     * 功能描述：Redis基础处理对象
     *
     * @param iRedisLockManager Redis——锁处理接口
     * @author : 王雷
     * @date : 2020/11/26 0026 下午 4:48
     */
    protected RedisSetManagerImpl(IRedisLockManager iRedisLockManager) {
        super(iRedisLockManager);
    }

    /**
     * 功能描述：获取Hash处理对象完成自定义处理
     *
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> SetOperations<String, T> giveSetOperations() {
        return (SetOperations<String, T>) giveRedisTemplate().opsForSet();
    }

    /**
     * 功能描述：添加数据到指定的Key中
     *
     * @param key   需要插入的Key
     * @param value 需要插入的内容
     * @param time  失效时间，单位：秒
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, T value, long time) {
        giveRedisTemplate().boundSetOps(key).add(value);
        if (time > 0L) {
            expire(key, time);
        }
    }

    /**
     * 功能描述：添加数据到指定的Key中，不限制时间
     *
     * @param key   需要插入的Key
     * @param value 需要插入的内容
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, T value) {
        addValueByKey(key, value, 0L);
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    @Override
    public <T> void sdel(String key, T... item) {
        giveSetOperations().remove(key, (Object) item);
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    @Override
    public <T> boolean sHasKey(String key, T item) {
        Assert.notNull(key, "Key不能为空");
        Assert.notNull(item, "内容不能为空");
        final Boolean member = giveSetOperations().isMember(key, item);
        return (member == null) ? Boolean.FALSE : member;
    }

    /**
     * 功能描述：获取存储的长度
     *
     * @param key 存储主键
     * @author : 王雷
     * @date : 2019/10/17 0017 下午 1:58
     */
    @Override
    public long giveSetSize(String key) {
        final Long size = giveSetOperations().size(key);
        return (size == null) ? 0L : size;
    }

    /**
     * 获取数据
     *
     * @param key 键
     * @return 值
     */
    @Override
    public <T> Set<T> giveValueByKey(String key) {
        return (Set<T>) giveSetOperations().members(key);
    }

    /**
     * 功能描述：Set集合交集运算
     *
     * @param key  目标Key
     * @param keys 需要对比的Key
     * @return : 交集结果
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 6:04
     */
    @Override
    public <T> Set<T> intersectionOperation(String key, String... keys) {
        return (Set<T>) giveSetOperations().intersect(key, Arrays.asList(keys));
    }

    /**
     * 功能描述：Set集合交集运算并将结果放入到新的集合中
     *
     * @param key     目标Key
     * @param destKey 新目标Key
     * @param keys    需要对比的Key
     * @return : 交集结果数量
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 6:04
     */
    @Override
    public long intersectionOperation(String key, String destKey, String... keys) {
        destKey = (destKey == null) ? "" : destKey;
        if ("".equals(destKey)) {
            return 0L;
        }
        final Long aLong = giveSetOperations().intersectAndStore(key, Arrays.asList(keys), destKey);
        return (aLong == null) ? 0L : aLong;
    }

    /**
     * 功能描述：Set集合并集运算
     *
     * @param key  目标Key
     * @param keys 需要对比的Key
     * @return : 交集结果
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 6:04
     */
    @Override
    public <T> Set<T> unionSetOperation(String key, String... keys) {
        return (Set<T>) giveSetOperations().union(key, Arrays.asList(keys));
    }

    /**
     * 功能描述：Set集合并集运算并将结果放入到新的集合中
     *
     * @param key     目标Key
     * @param destKey 新目标Key
     * @param keys    需要对比的Key
     * @return : 交集结果数量
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 6:04
     */
    @Override
    public long unionSetOperation(String key, String destKey, String... keys) {
        destKey = (destKey == null) ? "" : destKey;
        if ("".equals(destKey)) {
            return 0L;
        }
        final Long aLong = giveSetOperations().unionAndStore(key, Arrays.asList(keys), destKey);
        return (aLong == null) ? 0L : aLong;
    }

    /**
     * 功能描述：Set集合差集运算
     *
     * @param key  目标Key
     * @param keys 需要对比的Key
     * @return : 交集结果
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 6:04
     */
    @Override
    public <T> Set<T> differenceSetOperation(String key, String... keys) {
        return (Set<T>) giveSetOperations().difference(key, Arrays.asList(keys));
    }

    /**
     * 功能描述：Set集合差集运算并将结果放入到新的集合中
     *
     * @param key     目标Key
     * @param destKey 新目标Key
     * @param keys    需要对比的Key
     * @return : 交集结果数量
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 6:04
     */
    @Override
    public long differenceSetOperation(String key, String destKey, String... keys) {
        destKey = (destKey == null) ? "" : destKey;
        if ("".equals(destKey)) {
            return 0L;
        }
        final Long aLong = giveSetOperations().differenceAndStore(key, Arrays.asList(keys), destKey);
        return (aLong == null) ? 0L : aLong;
    }

    /**
     * 功能描述：随机提取Set集合中的数据
     *
     * @param key          目标Key
     * @param size         提取数据的长度
     * @param isDeleteData 是否删除提取的数据
     * @return : 提取的结果
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 6:22
     */
    @Override
    public <T> Collection<T> giveDataToRandom(String key, long size, boolean isDeleteData) {
        if (isDeleteData) {
            return (Collection<T>) giveSetOperations().distinctRandomMembers(key, size);
        }
        return (Collection<T>) giveSetOperations().pop(key, size);
    }

//    /**
//     * 属性描述：访问空值Key前缀
//     *
//     * @date : 2020/11/30 0030 下午 6:20
//     */
//    protected static final String SET_VISIT_NULL_LOCK_PREFIX = "Set_Visit_Null_Lock_Prefix_";

    /**
     * 属性描述：业务处理锁前缀
     *
     * @date : 2020/11/30 0030 下午 6:20
     */
    protected static final String SET_MANAGER_NULL_LOCK_PREFIX = "Set_Manager_Null_Lock_Prefix_";

    /**
     * 获取数据，带锁处理
     *
     * @param key           键
     * @param giveCacheData 数据无效时的回调函数
     * @param putMaxTime    存储的时间
     * @return 值
     */
    @Override
    public <T> Set<T> giveValueByKeyToLock(String key, GiveCacheData<Set<T>> giveCacheData, Long putMaxTime) throws Exception {
        /*
         * 第一步：检查访问缓存对象中是否有数据
         * 访问缓存中的数据是在没有获取到值或者逻辑处理出现异常的时候才会存在；
         * 存在的时间为1分钟；
         * */
        Set<T> visitDataMap = giveValueByKey(key);
        if (visitDataMap == null) {
            return iRedisLockManager.autoLockToManager(SET_MANAGER_NULL_LOCK_PREFIX + key
                    , 0, redisTemplate -> {
                        //再次验证值是否存在
                        final Set<T> verifyData = giveValueByKey(key);
                        if (verifyData != null) {
                            return verifyData;
                        } else {
                            final Set<T> stringObjectSet = giveCacheData.giveManager();
                            if (stringObjectSet != null && !stringObjectSet.isEmpty()) {
                                //将数据装入缓存中，默认保存的时间为60秒
                                addValueByKey(key, stringObjectSet, (putMaxTime == null || putMaxTime <= 0L) ? 60L : putMaxTime);
                            }
                            return stringObjectSet;
                        }
                    });
        }
        //说明之前已经处理过，没有达到重试的阀值
        return visitDataMap;
    }

    /**
     * 获取数据，带锁处理
     *
     * @param key           键
     * @param giveCacheData 数据无效时的回调函数
     * @return 值
     */
    @Override
    public <T> Set<T> giveValueByKeyToLock(String key, GiveCacheData<Set<T>> giveCacheData) throws Exception {
        return giveValueByKeyToLock(key, giveCacheData, null);
    }
}
